﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;

using DashFire;
using UnityEngine;
using UnityEditor;
using System.IO;

namespace DashFire.Assetbundle
{
  internal class ExportTargetInfo
  {
    internal int m_TargetIndex = -1;
    internal string m_TargetType = string.Empty;
    internal string m_TargetDir = string.Empty;
    internal string m_TargetParrern = "*.*";
  }
  internal class ExportResInfo
  {
    internal int m_Id = -1;
    internal string m_Type = string.Empty;
    internal string m_AssetName = string.Empty;
    internal string m_AssetShortName = string.Empty;
    internal string m_PackageName = string.Empty;
    internal string m_MD5 = string.Empty;
    internal long m_Size = 0;
    internal string m_Guid = string.Empty;
    //internal string m_DependGuid = string.Empty;
    internal HashSet<string> m_DependGuid = null;
    internal string m_LoadDependGuid = string.Empty;

    internal bool m_IsNeedBuild = false;

    internal string FormatDependGuid()
    {
      string ret = string.Empty;
      if (m_DependGuid != null && m_DependGuid.Count > 0) {
        foreach (string guid in m_DependGuid) {
          ret += guid + ";";
        }
      }
      return ret;
    }
  }
  internal class ExportConfig
  {
    // Game Config
    internal static string s_Version = "1.0.1";
    internal static string s_GroupName = "dev";
    internal static string s_ResServerURL = "http://192.168.1.30";

    // Target Config
    internal static int s_ExportTargetCount = 0;
    internal static List<ExportTargetInfo> s_ExportTargetList = new List<ExportTargetInfo>();

    // Build Res Option
    internal static BuildTarget s_BuildOptionTarget = BuildTarget.StandaloneWindows;
    internal static BuildAssetBundleOptions s_BuildOptionRes =
                      BuildAssetBundleOptions.CollectDependencies |
                      BuildAssetBundleOptions.DeterministicAssetBundle |
                      BuildAssetBundleOptions.CompleteAssets |
                      BuildAssetBundleOptions.UncompressedAssetBundle;
    internal static BuildOptions s_BuildOptionScene =
                    BuildOptions.UncompressedAssetBundle;
    internal static string s_BuildOptionExtend = ".ab";
    internal static bool s_BuildOptionZip = true;
    internal static bool s_IsKeepTargetPackageOnly = true;
#if (UNITY_IPHONE || UNITY_ANDROID)
    internal static bool s_IsDeployClean = true;
#else
    internal static bool s_IsDeployClean = false;
#endif
    // ExportInfo
    internal static Dictionary<string, ExportResInfo> s_ExportResDict = new Dictionary<string, ExportResInfo>();
    internal static List<string> s_DiffContent = new List<string>();
    internal static bool s_IsSupportUIUpdage = false;

    // Other
    internal static string s_OutputPath = "Assets/AssetBundle/build";
    internal static string s_ConfigFile = "AssetBundle/Config.txt";
    internal static string s_LogFile = "Assets/AssetBundle/build/BuildLog.txt";
    internal static string[] s_ConfigSplit = new string[] { ";", "|", };
    internal static string[] s_ExcludeExt = new string[] { ".cs", ".js" };
    internal static int s_ResIdGen = 0;

    internal static bool InitEnvironment()
    {
      //Init Log File
      if (File.Exists(s_LogFile)) {
        File.Delete(s_LogFile);
      }
      string logDirName = Path.GetDirectoryName(s_LogFile);
      if (!Directory.Exists(logDirName)) {
        Directory.CreateDirectory(logDirName);
      }

      ExportConfig.Log("--------------------------------------------------------------");
      ExportConfig.Log("ExportConfig InitEnvironment Start...");

      s_ExportTargetList.Clear();
      s_ExportResDict.Clear();

      return true;
    }
    internal static bool Load()
    {
      try {
        if (!InitEnvironment()) {
          Log("InitEnvironment failed.");
          return false;
        }

        string filePath = ExportConfig.PathCombine(Application.dataPath, s_ConfigFile);
        ConfigFile iniFile = new ConfigFile(filePath);

        //GameConfig
        s_Version = iniFile.GetSetting("GameConfig", "Version");
        s_GroupName = iniFile.GetSetting("GameConfig", "GroupName");
        s_ResServerURL = iniFile.GetSetting("GameConfig", "ResServerURL");

        s_BuildOptionTarget = ConvertStrToBuildTarget(iniFile.GetSetting("BuildOption", "BuildOptionTarget"));

        // Target Config
        string strExportTargetCount = iniFile.GetSetting("BuildConfig", "ExportTargetCount").Trim();
        if (!int.TryParse(strExportTargetCount, out s_ExportTargetCount)) {
          s_ExportTargetCount = 0;
        }
        s_ExportTargetList.Clear();
        for (int index = 1; index <= s_ExportTargetCount; index++) {
          ExportTargetInfo info = new ExportTargetInfo();
          info.m_TargetIndex = index;
          string resBuildDirs = iniFile.GetSetting("BuildConfig", "TargetDir" + index);
          info.m_TargetDir = resBuildDirs;
          string resBuildType = iniFile.GetSetting("BuildConfig", "TargetType" + index);
          info.m_TargetType = resBuildType;
          string resBuildPattern = iniFile.GetSetting("BuildConfig", "TargetParrern" + index);
          info.m_TargetParrern = resBuildPattern;
          s_ExportTargetList.Add(info);
        }
      } catch (System.Exception ex) {
        Log("ResBuildConfig.Load failed, ex:" + ex);
        return false;
      }
      Log("ResBuildConfig.Load Success");
      return true;
    }
    internal static bool Save()
    {
      try {
        string filePath = ExportConfig.PathCombine(Application.dataPath, s_ConfigFile);
        ConfigFile iniFile = new ConfigFile(filePath);

        //GameConfig
        iniFile.AddSetting("GameConfig", "Version", s_Version);
        iniFile.AddSetting("GameConfig", "GroupName", s_GroupName);
        iniFile.AddSetting("GameConfig", "ResServerURL", s_ResServerURL);

        iniFile.AddSetting("BuildOption", "BuildOptionTarget", ConvertBuildTargetToStr(s_BuildOptionTarget));

        //Target Config
        iniFile.AddSetting("BuildConfig", "ExportTargetCount", s_ExportTargetCount.ToString());
        for (int index = 0; index < s_ExportTargetCount; index++) {
          ExportTargetInfo info = s_ExportTargetList[index];
          iniFile.AddSetting("BuildConfig", "TargetType" + (info.m_TargetIndex), info.m_TargetType);
          iniFile.AddSetting("BuildConfig", "TargetDir" + (info.m_TargetIndex), info.m_TargetDir);
          iniFile.AddSetting("BuildConfig", "TargetParrern" + (info.m_TargetIndex), info.m_TargetParrern);
        }

        iniFile.SaveSettings();
      } catch (System.Exception ex) {
        Log("ResBuildConfig.Save failed, ex:" + ex);
        return false;
      }

      Log("ResBuildConfig.Save Success");
      return true;
    }

    /************************************************************************/
    /* Helper Func                                                          */
    /************************************************************************/
    internal static string GetPlatformPath()
    {
      string SavePath = string.Format("{0}/{1}/{2}/{3}",
            s_OutputPath,
            s_GroupName,
            GetChannelPlatformName(s_BuildOptionTarget),
            s_Version);
      return SavePath;
    }
    internal static string GetTmpPlatformPath()
    {
      string SavePath = string.Format("{0}/{1}/{2}/{3}",
            s_OutputPath,
            s_GroupName,
            GetTmpChannelPlatformName(s_BuildOptionTarget),
            s_Version);
      return SavePath;
    }
    internal static string GetPackagePath()
    {
      string SavePath = string.Format("{0}/{1}/{2}",
            s_OutputPath,
            s_GroupName,
            GetChannelPlatformName(s_BuildOptionTarget));
      return SavePath;
    }
    internal static string GetTmpPackagePath()
    {
      string SavePath = string.Format("{0}/{1}/{2}",
            s_OutputPath,
            s_GroupName,
            GetTmpChannelPlatformName(s_BuildOptionTarget));
      return SavePath;
    }
    public static string GetGroupPath()
    {
      string SavePath = string.Format("{0}/{1}",
            s_OutputPath,
            s_GroupName);
      return SavePath;
    }
    internal static string FormatResPackageFilePath()
    {
      return string.Format("{0}/{1}.zip",
        GetPackagePath(),
        s_Version);
    }
    internal static string GetChannelPlatformName(BuildTarget target)
    {
      return string.Format("{0}_{1}", GetPlatformName(target), "res");
    }
    internal static string GetTmpChannelPlatformName(BuildTarget target)
    {
      return string.Format("{0}_{1}_{2}", GetPlatformName(target), "res", "tmp");
    }
    internal static List<ExportTargetInfo> SearchExportTargetInfo(string type)
    {
      List<ExportTargetInfo> retList = new List<ExportTargetInfo>();
      foreach (ExportTargetInfo info in s_ExportTargetList) {
        if (info.m_TargetType == type) {
          retList.Add(info);
        }
      }
      return retList;
    }
    internal static int GenNextResId()
    {
      return s_ResIdGen++;
    }
    internal static void Log(string format, params object[] args)
    {
      string curTime = string.Format("[{0}]", System.DateTime.Now.ToString("MM-dd-HH-mm-ss"));
      string str = string.Format(curTime + format + "\n", args);
      File.AppendAllText(s_LogFile, str, System.Text.Encoding.UTF8);
    }
    internal static BuildTarget ConvertStrToBuildTarget(string content)
    {
      BuildTarget ret = BuildTarget.StandaloneWindows;
      try {
        ret = (BuildTarget)Enum.Parse(typeof(BuildTarget), content);
      } catch (Exception ex) {
        Log("ConvertStrToBuildOption error:" + content + " ex:" + ex);
      }
      return ret;
    }
    internal static string ConvertBuildTargetToStr(BuildTarget content)
    {
      return Enum.GetName(typeof(BuildTarget), content);
    }
    internal static string GetPlatformName(BuildTarget target)
    {
      switch (target) {
        case BuildTarget.StandaloneWindows:
        case BuildTarget.StandaloneWindows64: {
            return "win32";
          };
        case BuildTarget.StandaloneOSXIntel:
        case BuildTarget.StandaloneOSXIntel64:
        case BuildTarget.StandaloneOSXUniversal: {
            return "mac";
          };
        case BuildTarget.Android: {
            return "android";
          };
        case BuildTarget.iOS: {
            return "ios";
          };
        default: {
            return "invalid";
          }
      }
    }
    internal static string GetForceDownloadUrl(BuildTarget target)
    {
      switch (target) {
        case BuildTarget.Android: {
            return s_ResServerURL + "/" + "[group]/df_[group]_[channel].apk";
          }
        case BuildTarget.iOS: {
#if UNITY_APPSTORE
        return "https://itunes.apple.com/cn/app/id955307313";
#else
            return "itms-services://?action=download-manifest&amp;url=https://topic.changyou.com/mjzr/[group]/df_[group]_[channel].plist";
#endif
          }
        case BuildTarget.StandaloneWindows: {
            return s_ResServerURL + "/download.html";
          }
      }
      return string.Empty;
    }
    /************************************************************************/
    /* Helper Func                                                         */
    /************************************************************************/
    internal static string GetFilePathAbs(string filePath)
    {
      filePath = filePath.Replace("\\", "/");
      if (Path.IsPathRooted(filePath)) {
        return filePath;
      } else {
        return ExportConfig.PathCombine(Application.dataPath + "/..", filePath);
      }
    }
    internal static string FormatPackageName(string assetPath)
    {
      string guid = AssetDatabase.AssetPathToGUID(assetPath);
      string dirPath = guid.Substring(0, 2);
      return string.Format("{0}/{1}{2}", dirPath, guid, ResBuildConfig.BuildOptionExtend);
    }
    internal static string FormatResourceName(string assetPath)
    {
      assetPath = assetPath.Replace("\\", "/");
      if (string.IsNullOrEmpty(assetPath)) {
        ExportConfig.Log("TranslateFilePathToAssetPath filePath invalid filePath:" + assetPath);
        return string.Empty;
      }
      assetPath = assetPath.Trim();
      int assetsIndex = assetPath.ToLower().IndexOf("assets");
      if (assetsIndex < 0) {
        ExportConfig.Log("TranslateFilePathToAssetPath filePath invalid filePath:" + assetPath);
        return string.Empty;
      }
      return assetPath.Substring(assetsIndex);
    }
    internal static string FormatAssetShortName(string assetPath)
    {
      assetPath = assetPath.ToLower();
      if (assetPath.EndsWith(".unity")) {
        return Path.GetFileNameWithoutExtension(assetPath);
      } else {
        int startIndex = assetPath.LastIndexOf("/resources/");
        int endIndex = assetPath.LastIndexOf(".");
        if (startIndex >= 0 && endIndex >= 0) {
          startIndex += 11;
          return assetPath.Substring(startIndex, (endIndex - startIndex));
        } else {
          return assetPath;
        }
      }
    }
    internal static string FormatDependency(string assetPath)
    {
      string ret = string.Empty;
      string tmpGuid = string.Empty;
      bool isExclude = false;
      string[] dependList = AssetDatabase.GetDependencies(new string[] { assetPath });
      if (dependList != null && dependList.Length > 0) {
        foreach (string itemPath in dependList) {
          isExclude = false;
          tmpGuid = string.Empty;
          foreach (string ext in ExportConfig.s_ExcludeExt) {
            if (itemPath.EndsWith(ext)) {
              isExclude = true;
              break;
            }
          }
          if (!isExclude) {
            tmpGuid = AssetDatabase.AssetPathToGUID(itemPath);
            if (!string.IsNullOrEmpty(tmpGuid)) {
              ret += tmpGuid + ";";
            }
          }
        }
      }
      return ret;
    }
    internal static HashSet<string> FormatDependencySet(string assetPath)
    {
      HashSet<string> retSet = new HashSet<string>();
      string tmpGuid = string.Empty;
      bool isExclude = false;
      string[] dependList = AssetDatabase.GetDependencies(new string[] { assetPath });
      if (dependList != null && dependList.Length > 0) {
        foreach (string itemPath in dependList) {
          isExclude = false;
          tmpGuid = string.Empty;
          foreach (string ext in ExportConfig.s_ExcludeExt) {
            if (itemPath.EndsWith(ext)) {
              isExclude = true;
              break;
            }
          }
          if (!isExclude) {
            tmpGuid = AssetDatabase.AssetPathToGUID(itemPath);
            if (!string.IsNullOrEmpty(tmpGuid)) {
              retSet.Add(tmpGuid);
            }
          }
        }
      }
      return retSet;
    }
    internal static List<string> FormatContent(string restype)
    {
      List<string> tAssetContent = new List<string>();
      List<ExportTargetInfo> targetList = ExportConfig.SearchExportTargetInfo(restype);
      if (targetList != null && targetList.Count > 0) {
        foreach (ExportTargetInfo config in targetList) {
          DirectoryInfo tTargetDir = new DirectoryInfo(config.m_TargetDir);
          if (!tTargetDir.Exists) {
            Log("FormatContent not exist:" + config.m_TargetDir);
            continue;
          }

          string[] tResBuildPattern = config.m_TargetParrern.Split(ExportConfig.s_ConfigSplit, StringSplitOptions.RemoveEmptyEntries);
          if (tResBuildPattern == null || tResBuildPattern.Length == 0) {
            ExportConfig.Log("FormatContent pattern error:" + config.m_TargetParrern);
            continue;
          }
          FileInfo[] tTargetFiles = tResBuildPattern
            .SelectMany(i => tTargetDir.GetFiles(i, SearchOption.AllDirectories))
            .Distinct().ToArray();
          if (tTargetFiles != null && tTargetFiles.Length > 0) {
            foreach (FileInfo curFileInfo in tTargetFiles) {
              tAssetContent.Add(FormatResourceName(curFileInfo.FullName));
            }
          }
        }
      }
      return tAssetContent;
    }
    internal static bool IsEndWith(string filePath, string[] pattern)
    {
      if (pattern == null || pattern.Length == 0) {
        return false;
      }
      foreach (string tPattern in pattern) {
        if (filePath.EndsWith(tPattern)) {
          return true;
        }
      }
      return false;
    }
    internal static bool DeleteDirectory(string target_dir)
    {
      try {
        DirectoryInfo dirInfo = new DirectoryInfo(target_dir);
        FileInfo[] files = dirInfo.GetFiles();
        DirectoryInfo[] dirs = dirInfo.GetDirectories();
        foreach (FileInfo file in files) {
          file.Attributes = FileAttributes.Normal;
          file.Delete();
        }
        foreach (DirectoryInfo dir in dirs) {
          if (!DeleteDirectory(dir.FullName)) {
            Log("DeleteDirectory.DeleteDirectory failed.dir:" + dir.FullName);
            //return false;
          }
        }
        dirInfo.Refresh();
        dirInfo.Delete(true);
      } catch (System.Exception ex) {
        Log("DeleteDirectory failed.ex:" + ex);
        //return false;
      }
      return true;
    }
    internal static bool CopyDir(string srcDir, string tgtDir)
    {
      DirectoryInfo source = new DirectoryInfo(srcDir);
      DirectoryInfo target = new DirectoryInfo(tgtDir);
      if (!source.Exists) {
        return false;
      }
      if (!target.Exists) {
        target.Create();
      }
      FileInfo[] files = source.GetFiles();
      for (int i = 0; i < files.Length; i++) {
        File.Copy(files[i].FullName, target.FullName + "/" + files[i].Name, true);
      }
      DirectoryInfo[] dirs = source.GetDirectories();
      for (int j = 0; j < dirs.Length; j++) {
        CopyDir(dirs[j].FullName, target.FullName + "/" + dirs[j].Name);
      }
      return true;
    }
    internal static string ConvertPathSlash(string filePath)
    {
      return filePath.Replace("\\", "/");
    }
    internal static string PathCombine(string path1, string path2)
    {
      string retPath = string.Empty;
      path1 = ConvertPathSlash(path1);
      path2 = ConvertPathSlash(path2);
      if (path1.EndsWith("/") && path2.StartsWith("/")) {
        retPath = path1 + path2.Substring(1);
      } else if (!path1.EndsWith("/") && !path2.StartsWith("/")) {
        retPath = path1 + "/" + path2;
      } else {
        retPath = path1 + path2;
      }
      retPath = ConvertPathSlash(retPath);
      return retPath;
    }
    internal static bool CheckAssetType(string assetPath, string resType)
    {
      List<ExportTargetInfo> targetInfoList = SearchExportTargetInfo(resType);
      if (targetInfoList != null && targetInfoList.Count > 0) {
        string assetPathLower = assetPath.ToLower();
        foreach (ExportTargetInfo info in targetInfoList) {
          if (assetPathLower.StartsWith(info.m_TargetDir.ToLower())) {
            return true;
          }
        }
      }
      return false;
    }
    internal static string GetTargetPath(ExportResInfo data)
    {
      if (s_IsKeepTargetPackageOnly && data.m_IsNeedBuild) {
        return string.Format("{0}/{1}", ExportConfig.GetPlatformPath(), data.m_PackageName);
      } else {
        return string.Format("{0}/{1}", ExportConfig.GetTmpPlatformPath(), data.m_PackageName);
      }
    }
  }
}
